import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { parse } from 'vue-docgen-api'
import type { ComponentDoc, EventDescriptor, PropDescriptor } from 'vue-docgen-api'
import MarkdownIt from 'markdown-it'
import fg from 'fast-glob'
import chokidar from 'chokidar'
import { ViteDevServer } from 'vite'

export async function extractVueCompInfo(vueFileFullPath: string) {
  const compInfo = await parse(vueFileFullPath)
  return compInfo
}

export function compInfoToMarkdown(compInfo: ComponentDoc) {
  const { displayName, props, expose, slots } = compInfo
  const propsMarkdown = buildPropsMarkdown(compInfo)
  const exposeMarkdown = buildExposeMarkdown(compInfo)
  const emitsMarkdown = buildEventsMarkdown(compInfo)
  const slotsMarkdown = buildSlotsMarkdown(compInfo)
  const markdownContent = `# ${displayName}

## Props

${propsMarkdown}

## Emits

${emitsMarkdown}

## Expose

${exposeMarkdown}

## Slots

${slotsMarkdown}
`
  return markdownContent
}

function buildEventsMarkdown(compInfo: ComponentDoc) {
  let content = `
| 名称 | 说明 |
| :-- | :-- |
`
  const { events } = compInfo
  events?.forEach(evt => {
    // console.log(evt)
    content += `| ${buildEvtNameMarkdown(evt)} | ${formatDescription(evt.description)} |\n`
  })
  return content
}
function buildEvtNameMarkdown(evt: EventDescriptor) {
  if (evt.type && evt.type.names) {
    return `\`${evt.name}(${evt.type.names})\``
  } else {
    return `${evt.name}`
  }
}

function buildExposeMarkdown(compInfo: ComponentDoc) {
  let content = `
| 名称 | 说明 |
| :-- | :-- |
`
  const { expose } = compInfo
  expose?.forEach(item => {
    // console.log(item)
    content += `| ${item.name} | ${formatDescription(item.description)} |\n`
  })
  return content
}
function buildSlotsMarkdown(compInfo: ComponentDoc) {
  let content = `
| 名称 | 说明 |
| :-- | :-- |
`
  const { slots } = compInfo
  slots?.forEach(slot => {
    // console.log(slot)
    content += `| ${slot.name} | ${formatDescription(slot.description)} |\n`
  })
  return content
}
function buildPropsMarkdown(compInfo: ComponentDoc) {
  let content = `
| 名称 | 说明 | 类型 | 默认值 |
| :-- | :-- |:-- |:-- |
`
  const { props } = compInfo
  if (!props) {
    return content
  }
  props.forEach(item => {
    // console.log(item.name, '-->', typeToMarkdown(item.type), item.type)
    content += `| ${item.name} ${item.required ? '<strong style="color:red">*</strong>' : ''} | ${formatDescription(
      item.description
    )} | ${typeToMarkdown(item.type)} | ${item.defaultValue?.value ?? ''} |\n`
  })
  return content
}
function formatDescription(description?: string) {
  if (!description) {
    return ''
  } else {
    // 为避免description中的换行符，对markdown解析造成混乱，这里将换行全部替换为\t，再在解析成html之后，再将\t替换为<br/>，最大程度保证显示效果
    return description.replace(/\n/g, '\t')
  }
}
function typeToMarkdown(type: PropDescriptor['type']) {
  if (!type) return ''

  if (type.name === 'Array') {
    // @ts-ignore
    if (type.elements) {
      // @ts-ignore
      if (type.elements.length === 1) {
        // @ts-ignore
        return `\`${type.name}<${type.elements[0].name}>\``
        // @ts-ignore
      } else if (type.elements > 1) {
        // @ts-ignore
        return `${type.elements.map(item => item)}`
      }
    }
  } else if (type.name === 'Map') {
    // @ts-ignore
    if (type.elements) {
      // @ts-ignore
      if (type.elements.length === 2) {
        // @ts-ignore
        return `\`${type.name}<${type.elements[0].name}, ${type.elements[1].name}>\``
      }
    }
  } else if (type.name === 'union') {
    // @ts-ignore
    if (type.elements) {
      // @ts-ignore
      return `\`${type.elements.map(item => item.name).join(' ｜ ')}\``
    }
  }
  return type.name
}

const md = MarkdownIt({
  html: true,
})
export function markdownToHTML(markdown: string) {
  return md.render(markdown)
}

const projectRoot = process.cwd()
console.log(`项目根目录:${projectRoot}`)
/**
 * 将相对路径转为绝对路径
 * @param relativePath 相对路径。只必须以`/`开头，表示项目根目录. 如: /src/hello.java 表示项目根目录下的src目录下的hello.java文件
 * @returns 文件的绝对路径
 */
export function toAbsolutePath(relativePath: string) {
  return join(projectRoot, relativePath)
}

/**
 * 读取文件内容
 * @param absoluteFilePath 文件绝对路径
 * @param encode 是否对文件内容进行encodeURI操作. 默认: true
 * @returns
 */
export function readFileContent(absoluteFilePath: string, encode = true) {
  const encoding = 'utf-8'
  let content = readFileSync(absoluteFilePath, { encoding })
  content = content.toString()
  if (encode) {
    content = encodeURI(content)
  }
  return content
}

export async function parseAllVueCompInfo(glob: string | string[]) {
  const list = fg.sync(glob, { cwd: projectRoot })
  console.log('list', list)
  const cache: Record<string, string> = Object.create({})
  for (let i = 0; i < list.length; i++) {
    const relativeVueFilePath = list[i]
    const absoluteVueFilePath = join(projectRoot, relativeVueFilePath)
    const compInfo = await extractVueCompInfo(absoluteVueFilePath)
    const markdown = compInfoToMarkdown(compInfo)
    // 这里将\t转为<br/>是因为, 前面为了将所有的\n转为了\t
    const html = markdownToHTML(markdown).replace(/\t/g, '<br/>')
    cache[absoluteVueFilePath] = html
    // console.log(absoluteVueFilePath)
  }
  return cache
}

export function watchVueFileChange(glob: string | string[], server: ViteDevServer) {
  const watcher = chokidar.watch(glob, {
    ignoreInitial: true,
    cwd: projectRoot,
  })
  watcher.on('all', (eventName, path) => {
    console.log('监听到vue文件变更，自动重启vite服务', eventName, path)
    server.restart()
  })
  return watcher
}
